package org.example.queue.delayQueue;

import org.jetbrains.annotations.NotNull;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;

/**
 * 订单延迟对象信息
 *
 * @author wangMaoXiong
 * @version 1.0
 * @date 2021/8/28 14:58
 */
public class OrderDelayed implements Delayed {

    private String orderId;//订单ID
    private String orderName;//订单名称
    private long timeout;//任务超时时间(毫秒)
    private long pointOfTime;//任务超时的时间点，表示在这个时间点这个任务超时，比如 2021-08-28 14:00:00

    private OrderDelayed() {

    }

    /**
     * @param orderId   ：订单 ID
     * @param orderName ：订单 名称
     * @param timeout   ：任务超时时间，表示多少时间后任务超时，比如 30分钟
     * @param timeUnit  ：超时时间的单位，比如秒，毫秒，微妙，纳秒等
     */
    public OrderDelayed(String orderId, String orderName, long timeout, TimeUnit timeUnit) {
        this.orderId = orderId;
        this.orderName = orderName;
        this.timeout = TimeUnit.MILLISECONDS.convert(timeout, timeUnit);
        this.pointOfTime = this.timeout + System.currentTimeMillis();
    }

    /**
     * 获取任务距离超时时间还剩下多久，约定单位为毫秒。
     * 当使用 take 阻塞方法获取队列中的元素时，内部会自动调用本方法进行时间对比，当返回的值小于等于0时，才会获取元素。
     * take 内部使用的纳秒时间级别.
     * long convert(long sourceDuration, TimeUnit sourceUnit)：
     * 1：将给定单位(sourceUnit)中的给定持续时间转换为此单位，从细粒度到粗粒度的转换会截断，因此会失去精度,例如将 999 毫秒转换为秒会产生 0
     * 2：例如要将10分钟转换为毫秒，请使用：TimeUnit.MILLISECONDS.convert(10L,TimeUnit.MINUTES)
     * 3：返回此单位中转换的持续时间，如果转换将负溢出，则返回 Long.MIN_VALUE；如果转换将正溢出，则返回 Long.MAX_VALUE
     *
     * @param unit ：剩余时间的单位，比如 分，秒，毫秒、纳秒等
     * @return
     */
    @Override
    public long getDelay(@NotNull TimeUnit unit) {
        return unit.convert(pointOfTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
    }

    /**
     * 任务比较
     *
     * @param other
     * @return
     */
    @Override
    public int compareTo(@NotNull Delayed other) {
        if (other == this) {
            return 0;
        }
        if (other instanceof OrderDelayed) {
            OrderDelayed t = (OrderDelayed) other;
            long d = getDelay(TimeUnit.MILLISECONDS) - t.getDelay(TimeUnit.MILLISECONDS);
            return (d == 0) ? 0 : ((d < 0) ? -1 : 1);
        }
        return -1;
    }

    @Override
    public String toString() {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        return "OrderDelayed{" +
                "orderId='" + orderId + '\'' +
                ", orderName='" + orderName + '\'' +
                ", timeout(毫秒)='" + timeout + '\'' +
                ", pointOfTime=" + dateFormat.format(new Date(pointOfTime)) +
                '}';
    }
}
